﻿using System;
using System.IO;
using System.Xml;
using System.Xml.Serialization;
using Microsoft.WowAddonStudio.FrameXml.Serialization;

namespace Microsoft.WowAddonStudio.FrameXml.Serialization.Support
{
    public class FrameXmlLoader
    {
        public const string WowNamespace = "http://www.blizzard.com/wow/ui/";

        /// <summary>
        /// Gets or sets the schema path.
        /// </summary>
        public string SchemaPath { get; set; }

        /// <summary>
        /// Loads the Ui from a FrameXML file and optionally validates.
        /// </summary>
        /// <param name="frameXmlPath">The FrameXML path.</param>
        /// <param name="validate">True, if the FrameXML should validated; false otherwise.</param>
        /// <returns>An instance of the <see cref="Ui"/> class.</returns>
        public Ui LoadUi(string frameXmlPath, bool validate)
        {
            if (frameXmlPath == null)
                throw new ArgumentNullException("frameXmlPath");
            if (validate && !File.Exists(this.SchemaPath))
                throw new InvalidOperationException("Cannot validate FrameXML without schema.");

            var serializer = new XmlSerializer(typeof(Ui));

            // Deserialize FrameXML using the specified reader settings
            using (var xmlReader = XmlReader.Create(frameXmlPath, CreateXmlReaderSettings(validate)))
            {
                var ui = (Ui)serializer.Deserialize(xmlReader);

                ui.InitializeParent();

                return ui;
            }
        }

		/// <summary>
		/// Loads the UI.
		/// </summary>
		/// <param name="frameXmlStream">The frame XML stream.</param>
		/// <param name="validate">if set to <c>true</c> [validate].</param>
		/// <returns></returns>
		public Ui LoadUi(Stream frameXmlStream, bool validate)
		{
			if (frameXmlStream == null)
				throw new ArgumentNullException("frameXmlStream");
			if (validate && !File.Exists(this.SchemaPath))
				throw new InvalidOperationException("Cannot validate FrameXML without schema.");

			var serializer = new XmlSerializer(typeof(Ui));

			// Deserialize FrameXML using the specified reader settings
			using (var xmlReader = XmlReader.Create(frameXmlStream, CreateXmlReaderSettings(validate)))
			{
				var ui = (Ui)serializer.Deserialize(xmlReader);

				ui.InitializeParent();

				return ui;
			}
		}

		/// <summary>
		/// Loads the UI.
		/// </summary>
		/// <param name="frameXmlStream">The frame XML stream.</param>
		/// <returns></returns>
		public Ui LoadUi(Stream frameXmlStream)
		{
			return this.LoadUi(frameXmlStream, !String.IsNullOrEmpty(this.SchemaPath));
		}

        /// <summary>
        /// Loads the Ui from a FrameXML file and validates if <see cref="SchemaPath"/> is set.
        /// </summary>
        /// <param name="frameXmlPath">The FrameXML path.</param>
        /// <returns>An instance of the <see cref="Ui"/> class.</returns>
        public Ui LoadUi(string frameXmlPath)
        {
            return this.LoadUi(frameXmlPath, !String.IsNullOrEmpty(this.SchemaPath));
        }

        /// <summary>
        /// Creates <see cref="XmlReaderSettings"/> for reading FrameXML files.
        /// </summary>
        /// <param name="validate">A value indicating whether the <see cref="XmlReader"/> should validate the FrameXML.</param>
        /// <returns>An instance of the <see cref="XmlReaderSettings"/> class.</returns>
        public XmlReaderSettings CreateXmlReaderSettings(bool validate)
        {
            var xmlReaderSettings = new XmlReaderSettings
                                        {
                                            IgnoreComments = true,
                                            ValidationType = validate ? ValidationType.Schema : ValidationType.None,
											IgnoreProcessingInstructions = true
                                        };

            if (validate)
            {
                using (Stream stream = new FileStream(this.SchemaPath, FileMode.Open, FileAccess.Read))
                using (XmlReader reader = XmlReader.Create(stream))
                {
                    xmlReaderSettings.Schemas.Add(WowNamespace, reader);
                }
            }

            return xmlReaderSettings;
        }
    }
}
